﻿/* 
This file is part of Ingo Karstein's Web based PowerShell Console project

**Do not remove this comment**

Please see the project homepage at CodePlex:
  http://webpsconsole.codeplex.com/

Please see my blog:
  http://ikarstein.wordpress.com

Licence: GPLv2
    Ingo Karstein's SharePoint 2010 Custom Ribbon Demo
    Copyright (C) 2011  Ingo Karstein, ikarstein@hotmail.com

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Threading;
using System.Drawing;
using System.Management.Automation;
using System.Management.Automation.Runspaces;
using System.Text;
using System.Management.Automation.Host;
using System.Collections;
using System.Web.SessionState;
using Newtonsoft.Json;

namespace ik.PowerShell.WebPSConsole
{
    internal class Server : IDisposable, WebPSConsoleApp
    {
        private HttpSessionState session;
        private Thread workerThread = null;
        private Thread pipelineThread = null;
    
        private ManualResetEvent stopEvent;
        private ManualResetEvent stoppedEvent;
        private Queue<InputObject> inputQueue;
        private Queue<OutputObject> outputQueue;

        WebPSConsoleHostUI ui = null;
        WebPSConsoleHost host = null;

        System.Management.Automation.PowerShell _ps = null;
        Runspace _runspace = null;
        Pipeline currentPipeline = null;

        private bool powerShellIsRunning = false;
        private Guid currentCorrlationId = Guid.Empty;

        public event ResizeHandler OnResize;
        public delegate void ResizeHandler(int width, int height);

        public event UserInputHandler OnUserInput;
        public delegate void UserInputHandler(string inputCorrelationId, string inputData);

        public event TitleChangedHandler OnTitleChanged;
        public delegate void TitleChangedHandler(string title);

        public event ScrollPosChangedHandler OnScrollPosChanged;
        public delegate void ScrollPosChangedHandler(int x, int y);

        private Buffer outBuffer = null;
        private StringBuilder outputAsRTF = null;
        private Coordinates cursorPos = new Coordinates(0, 0);
        private System.Management.Automation.Host.Size windowSize = new System.Management.Automation.Host.Size(1000, 1000);
        private SortedList<MyColor, int> colorCode = null;

        private OutputObjectUserRequest lastUserRequest = null;
        private DateTime lastInput = DateTime.Now;

        private List<string> commandHistory = new List<string>();

        public Server(HttpSessionState session)
        {
            this.session = session;
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                stopEvent = new ManualResetEvent(false);
                stoppedEvent = new ManualResetEvent(false);
                inputQueue = new Queue<InputObject>();
                outputQueue = new Queue<OutputObject>();

                ThreadStart ts = new ThreadStart(worker);
                workerThread = new Thread(worker);
                workerThread.Name = "Server:Worker";
                workerThread.Start();
            }

            outBuffer = new Buffer();
            outputAsRTF = new StringBuilder();

            SortedList<int, MyColor> tmp = new SortedList<int, MyColor>();
            colorCode = new SortedList<MyColor, int>();
            tmp.Add(1, Color.Black.ToMyColor());
            tmp.Add(2, Color.White.ToMyColor());
            tmp.Add(3, Color.Blue.ToMyColor());
            tmp.Add(4, Color.Cyan.ToMyColor());
            tmp.Add(5, Color.DarkBlue.ToMyColor());
            tmp.Add(6, Color.DarkCyan.ToMyColor());
            tmp.Add(7, Color.DarkGray.ToMyColor());
            tmp.Add(8, Color.DarkGreen.ToMyColor());
            tmp.Add(9, Color.DarkMagenta.ToMyColor());
            tmp.Add(10, Color.DarkRed.ToMyColor());
            tmp.Add(11, Color.Gold.ToMyColor());
            tmp.Add(12, Color.Gray.ToMyColor());
            tmp.Add(13, Color.Green.ToMyColor());
            tmp.Add(14, Color.Magenta.ToMyColor());
            tmp.Add(15, Color.Red.ToMyColor());
            tmp.Add(16, Color.Yellow.ToMyColor());


            outputAsRTF.Append(@"{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fnil\fcharset0 Courier New;}}{\colortbl ;");
            foreach( int i in tmp.Keys )
            {
                MyColor c = tmp[i];
                outputAsRTF.Append(string.Format("\\red{0}\\green{1}\\blue{2};", c.Color.R, c.Color.G, c.Color.B));
                colorCode.Add(c, i);
            }
            outputAsRTF.Append(@"}{\*\generator ik.PowerShell.WebPSConsole 0.1.0.0;}\viewkind5\uc1{\*\background{\shp{\*\shpinst
{\sp{\sn fillType}{\sv 0}}
{\sp{\sn fillColor}{\sv 0}}
{\sp{\sn fillBackColor}{\sv 0}}
{\sp{\sn fillFocus}{\sv 0}}
{\sp{\sn fillBlip}{\sv {\pict\wmetafile0\picscalex1\picscaley1 
}}}}}}\pard\sl240\slmult1\cf1\lang7\f0\fs22 ");

            lastInput = DateTime.Now;
        }

        public void Stop()
        {
            Trace("Stop event received.");
            stopEvent.Set();
            stoppedEvent.WaitOne();
        }

        public string RTF
        {
            get
            {
                Trace("Send console as RTF  file");
                return outputAsRTF.ToString();
            }
        }

        private void ProcessInput(InputObject inp)
        {
            lastInput = DateTime.Now;
            if( inp is InputObjectConsoleDim )
                ProcessInputConsoleDim(inp);
            else if( inp is InputObjectPipeFinished )
                ProcessInputPipeFinished(inp);
            else if( inp is InputObjectPipeCancelEvent )
                ProcessInputObjectPipeCancelEvent(inp);
            else if( inp is InputObjectUserResponse )
                ProcessInputUserResponse(inp);
            else if( inp is InputObjectRestoreUserRequest )
                ProcessInputObjectRestoreUserRequest(inp);
            else
                ProcessInputDefault(inp);
        }

        private void ProcessInputObjectRestoreUserRequest(InputObject inp)
        {
            Trace("Resend last user request info...");
            ResendUserRequest();
        }

        private void ProcessInputObjectPipeCancelEvent(InputObject inp)
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                Trace("Got pipeline cancel event...");
                if( powerShellIsRunning && currentPipeline != null )
                {
                    currentPipeline.StopAsync();
                }
            }
        }

        private void ProcessInputPipeFinished(InputObject inp)
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                Trace("Pipeline finished");
                InputObjectPipeFinished i = (InputObjectPipeFinished)inp;
                if( i.psi.State == PipelineState.Failed )
                {
                    WriteFromPSEnvironment(i.psi.Reason.Message, ui.ErrorForegroundColor, ui.ErrorBackgroundColor, true);
                }
                AddOutputObject(new OutputObjectPipeFinished());
                powerShellIsRunning = false;
            }
        }

        private void ProcessInputUserResponse(InputObject inp)
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                Trace("Got user response");

                InputObjectUserResponse i = (InputObjectUserResponse)inp;
                if( OnUserInput != null )
                {
                    OnUserInput(i.requestCorrelationId, i.data);
                }
            }
        }

        private void ProcessInputConsoleDim(InputObject inp)
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                windowSize = new System.Management.Automation.Host.Size(( (InputObjectConsoleDim)inp ).Width, ( (InputObjectConsoleDim)inp ).Height);
                if( OnResize != null )
                    OnResize(windowSize.Width, windowSize.Height);

                Trace("Height: {0}, Width: {1}", windowSize.Height, windowSize.Width);
            }
        }

        private void ProcessInputDefault(InputObject inp)
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                Trace("Processing input...");

                CommandResponse rr = JsonConvert.DeserializeObject<CommandResponse>(inp.s);

                string command = rr.data;

                WriteFromPSEnvironment("PS> " + command, ui.DefaultForegroundColor, ui.DefaultBackgroundColor, true);

                currentPipeline = PSRunspace.CreatePipeline();
                currentPipeline.Commands.AddScript(command);
                Command c1 = new Command("Out-String");
                c1.Parameters.Add("Stream");
                currentPipeline.Commands.Add(c1);
                currentPipeline.Commands.Add("Out-WebPSConsole");

                currentPipeline.StateChanged += new EventHandler<PipelineStateEventArgs>(p_StateChanged);
                currentPipeline.Output.DataReady += new EventHandler(Output_DataReady);
                currentPipeline.Error.DataReady += new EventHandler(Error_DataReady);

                currentPipeline.InvokeAsync();
                powerShellIsRunning = true;
                currentCorrlationId = Guid.NewGuid();

                Trace("Pipe started<br/>&nbsp;&nbsp;Correlation Id: {0}", currentCorrlationId);
            }
        }

        void Error_DataReady(object sender, EventArgs e)
        {
            var p1 = ( (PipelineReader<object>)sender ).Read();
            if( p1 != null )
            {
                string msg = p1.ToString();
                if( !string.IsNullOrEmpty(msg) )
                    WriteFromPSEnvironment(msg, ui.ErrorForegroundColor, ui.ErrorBackgroundColor, true);
            }
        }

        void Output_DataReady(object sender, EventArgs e)
        {
            var p1 = ( (PipelineReader<PSObject>)sender ).Read();
            if( p1 != null )
                WriteFromPSEnvironment(p1.ToString(), ui.DefaultForegroundColor, ui.DefaultBackgroundColor, true);

        }

        internal void Trace(string msg, params object[] parameters)
        {
            OutputObjectTraceMessage o2 = new OutputObjectTraceMessage();
            o2.Message = string.Format(msg, parameters);
            outputQueue.Enqueue(o2);
        }

        void p_StateChanged(object sender, PipelineStateEventArgs e)
        {
            OutputObjectPipeState o = new OutputObjectPipeState
            {
                CorrelationId = currentCorrlationId.ToString(),
                PipeState = (int)e.PipelineStateInfo.State,
                Reason = ( e.PipelineStateInfo.Reason == null ? "" : e.PipelineStateInfo.Reason.Message )
            };

            outputQueue.Enqueue(o);

            Trace("Pipe state changed<br/>&nbsp;&nbsp;Correlation Id: {0}<br/>&nbsp;&nbsp;State: {1}<br/>&nbsp;&nbsp;Reason: {2}",
                        currentCorrlationId, e.PipelineStateInfo.State, ( e.PipelineStateInfo.Reason == null ? "" : e.PipelineStateInfo.Reason.Message ));

            if( e.PipelineStateInfo.State != PipelineState.Running && e.PipelineStateInfo.State != PipelineState.Stopping )
            {
                InputObjectPipeFinished i = new InputObjectPipeFinished(e.PipelineStateInfo);
                inputQueue.Enqueue(i);
            }
        }

        private void worker()
        {
            using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
            {
                ui = new WebPSConsoleHostUI(this);
                host = new WebPSConsoleHost(this, ui);

                bool timeoutWarningSend = false;
                while( !stopEvent.WaitOne(0) )
                {
                    if( inputQueue.Count > 0 )
                    {
                        InputObject i = inputQueue.Dequeue();

                        ProcessInput(i);
                    }
                    else
                    {
                        TimeSpan s = DateTime.Now.Subtract(lastInput);
                        int time = (int)s.TotalMinutes;
                        if(  time< Statics.SessionTimeoutWarning )
                        {
                            if( timeoutWarningSend )
                                timeoutWarningSend = false;
                        }
                        else if( time >= Statics.SessionTimeoutWarning && time < Statics.SessionTimeout )
                        {
                            if( !timeoutWarningSend )
                            {
                                WriteFromPSEnvironment(string.Format("[TIMEOUT IN {0} MINUTES: {1} MINUTES SINCE LAST INPUT.]",(Statics.SessionTimeout-Statics.SessionTimeoutWarning), time), ConsoleColor.White, ConsoleColor.Red, true);
                                timeoutWarningSend = true;
                            }
                        }
                        else if( time >= Statics.SessionTimeout )
                        {
                            WriteFromPSEnvironment(string.Format("[TIMEOUT OF SESSION: {0} MINUTES SINCE LAST INPUT. SESSION WILL BE RECYCLED NOW!]", Statics.SessionTimeout), ConsoleColor.White, ConsoleColor.Red, true);
                            outputQueue.Enqueue(new OutputObjectEndSession());
                            session.Abandon();
                            stopEvent.Set();
                            break;
                        }

                        int wait = ( (int)( s.TotalSeconds / 20 ) ) * 200;
                        if( wait < 100 )
                            wait = 100;
                        if( wait > 6000 )
                            wait = 6000;
                        Thread.Sleep(wait);
                    };
                }
            }
            stoppedEvent.Set();
        }

        internal bool PipelineAbortRequested
        {
            get
            {
                if( currentPipeline == null )
                    return false;
                if( currentPipeline.PipelineStateInfo.State == PipelineState.Stopping )
                    return true;
                return false;
            }
        }

        internal Runspace PSRunspace
        {
            get
            {
                using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
                {
                    if( _runspace == null )
                    {
                        Trace("Creating PowerShell Runspace");
                        RunspaceConfiguration rc = RunspaceConfiguration.Create();
                        //PSSnapInException warning;
                        //WebPSConsoleSnapIn s = new WebPSConsoleSnapIn();
                        //IDictionary mySavedState = new Hashtable();
                        //s.Install(mySavedState);

                        //rc.AddPSSnapIn("ik.PowerShell.WebPSConsole", out warning);

                        _runspace = RunspaceFactory.CreateRunspace(host);
                        _runspace.Open();

                        using( var p = _runspace.CreatePipeline() )
                        {
                            var c = new Command("Import-Module");
                            c.Parameters.Add(new CommandParameter("Assembly", this.GetType().Assembly));
                            p.Commands.Add(c);
                            var output = p.Invoke();
                        }
                    }
                }

                return _runspace;
            }
            set
            {
                if( _runspace != null )
                {
                    _runspace.Close();
                    _runspace.Dispose();
                    _runspace = null;
                }

            }
        }

        internal System.Management.Automation.PowerShell PS
        {
            get
            {
                using( var imp = System.Security.Principal.WindowsIdentity.GetCurrent().Impersonate() )
                {
                    if( _ps == null )
                    {
                        Trace("Creating PowerShell Environment");
                        _ps = System.Management.Automation.PowerShell.Create();
                        _ps.Runspace = PSRunspace;

                        _ps.Streams.Progress.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e)
                        {
                            ui.WriteLine(( (PSDataCollection<ProgressRecord>)sender )[e.Index].ToString());
                        });

                        _ps.Streams.Verbose.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e)
                        {
                            ui.WriteVerboseLine(( (PSDataCollection<VerboseRecord>)sender )[e.Index].ToString());
                        });

                        _ps.Streams.Warning.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e)
                        {
                            ui.WriteWarningLine(( (PSDataCollection<WarningRecord>)sender )[e.Index].ToString());
                        });

                        _ps.Streams.Error.DataAdded += new EventHandler<DataAddedEventArgs>(delegate(object sender, DataAddedEventArgs e)
                        {
                            ui.WriteErrorLine(( (PSDataCollection<ErrorRecord>)sender )[e.Index].ToString());
                        });
                    }
                }

                return _ps;
            }
            set
            {
                if( value == null )
                {
                    if( _ps != null )
                    {
                        _ps.Dispose();
                        _ps = null;
                    }
                    PSRunspace = null;
                }
            }
        }

        public void AddInputObject(InputObject input)
        {
            inputQueue.Enqueue(input);
        }

        public OutputObject GetOutputObject()
        {
            return outputQueue.Dequeue();
        }

        public int OutputObjectCount
        {
            get
            {
                return outputQueue.Count;
            }
        }

        public void WriteFromPSEnvironment(string Message)
        {
            WriteFromPSEnvironment(Message, DefaultForegroundColor, DefaultBackgroundColor, true);
        }

        public void WriteFromPSEnvironment(string Message, ConsoleColor ForegroundColor, ConsoleColor BackgroundColor, bool NewLine)
        {
            Color bc = Color.Black;
            switch( BackgroundColor )
            {
                case ConsoleColor.Black:
                    bc = Color.Black;
                    break;
                case ConsoleColor.Blue:
                    bc = Color.Blue;
                    break;
                case ConsoleColor.Cyan:
                    bc = Color.Cyan;
                    break;
                case ConsoleColor.DarkBlue:
                    bc = Color.DarkBlue;
                    break;
                case ConsoleColor.DarkCyan:
                    bc = Color.DarkCyan;
                    break;
                case ConsoleColor.DarkGray:
                    bc = Color.DarkGray;
                    break;
                case ConsoleColor.DarkGreen:
                    bc = Color.DarkGreen;
                    break;
                case ConsoleColor.DarkMagenta:
                    bc = Color.DarkMagenta;
                    break;
                case ConsoleColor.DarkRed:
                    bc = Color.DarkRed;
                    break;
                case ConsoleColor.DarkYellow:
                    bc = Color.Gold;
                    break;
                case ConsoleColor.Gray:
                    bc = Color.Gray;
                    break;
                case ConsoleColor.Green:
                    bc = Color.Green;
                    break;
                case ConsoleColor.Magenta:
                    bc = Color.Magenta;
                    break;
                case ConsoleColor.Red:
                    bc = Color.Red;
                    break;
                case ConsoleColor.White:
                    bc = Color.White;
                    break;
                case ConsoleColor.Yellow:
                    bc = Color.Yellow;
                    break;
            }

            Color fc = Color.White;
            switch( ForegroundColor )
            {
                case ConsoleColor.Black:
                    fc = Color.Black;
                    break;
                case ConsoleColor.Blue:
                    fc = Color.Blue;
                    break;
                case ConsoleColor.Cyan:
                    fc = Color.Cyan;
                    break;
                case ConsoleColor.DarkBlue:
                    fc = Color.DarkBlue;
                    break;
                case ConsoleColor.DarkCyan:
                    fc = Color.DarkCyan;
                    break;
                case ConsoleColor.DarkGray:
                    fc = Color.DarkGray;
                    break;
                case ConsoleColor.DarkGreen:
                    fc = Color.DarkGreen;
                    break;
                case ConsoleColor.DarkMagenta:
                    fc = Color.DarkMagenta;
                    break;
                case ConsoleColor.DarkRed:
                    fc = Color.DarkRed;
                    break;
                case ConsoleColor.DarkYellow:
                    fc = Color.Gold;
                    break;
                case ConsoleColor.Gray:
                    fc = Color.Gray;
                    break;
                case ConsoleColor.Green:
                    fc = Color.Green;
                    break;
                case ConsoleColor.Magenta:
                    fc = Color.Magenta;
                    break;
                case ConsoleColor.Red:
                    fc = Color.Red;
                    break;
                case ConsoleColor.White:
                    fc = Color.White;
                    break;
                case ConsoleColor.Yellow:
                    fc = Color.Yellow;
                    break;
            }

            Message = Message.TrimEnd(new char[] { ' ','\t'});

            StringBuilder forOutput = new StringBuilder(Message.Length);
            //forOutput.Append(Message);

            /* Maybe later a better buffer management */
            
            int x = cursorPos.X;
            int y = cursorPos.Y;
            for( int i = 0; i < Message.Length; i++ )
            {
                if( stopEvent.WaitOne(0) )
                    return;
                if( Message[i] == '\n' )
                {
                    forOutput.Append("<br/>");
                    y++;
                    x = 0;
                }
                else if( Message[i] == '\t' )
                {
                    forOutput.Append("<tab/>");
                }
                else if( (int)Message[i] < 32 )
                {
                }
                else
                {
                    outBuffer.SetCell(x,y, new BufferCell
                    {
                        BufferCellType = BufferCellType.Complete,
                        ForegroundColor = ForegroundColor,
                        BackgroundColor = BackgroundColor,
                        Character = Message[i]
                    });

                    if( Message[i] == ' ' )
                        forOutput.Append("&nbsp;");
                    else
                        forOutput.Append(Message[i]);

                    x++;
                    if( x > windowSize.Width )
                    {
                        forOutput.Append("<br/>");
                        y++;
                        x = 0;
                    }
                }

                if( y >= outBuffer.MaxRows )
                {
                    outBuffer.ScrollLinesUp(1);
                    y--;
                }
            }
            
            /*
            int x = cursorPos.X;
            int y = cursorPos.Y;
            for( int i = 0; i < Message.Length; i++ )
            {
                if( Message[i] == '\n' )
                {
                    Message.Insert(i, "\n");
                    i++;
                    y++;
                    x = 0;
                }
                else
                {
                    outBuffer.rows[y].cells[x] = new BufferCell
                        {
                            BufferCellType = BufferCellType.Complete,
                            ForegroundColor = ForegroundColor,
                            BackgroundColor = BackgroundColor,
                            Character = Message[i]
                        };
                    x++;
                }

                if( x > outBuffer.MaxCells )
                {
                        
                    y++;
                    x = 0;
                }

                if( y >= outBuffer.MaxRows )
                {
                    outBuffer.ScrollLinesUp(1);
                    y--;
                }
            }
            */

            if( NewLine )
            {
                x = 0;
                y++;
            }

            if( y >= outBuffer.MaxRows )
            {
                outBuffer.ScrollLinesUp(1);
                y--;
            }

            cursorPos = new Coordinates(x, y);

            outputAsRTF.Append(string.Format(@"\cf{0}\hightlight{1} {2}\cf0\hightlight0", colorCode[fc.ToMyColor()], colorCode[bc.ToMyColor()], Message.Replace("\\","\\\\").Replace("\r\n", "\n").Replace("\n", " \\par ")));

            if( NewLine )
                outputAsRTF.Append("\\par");

            OutputObjectMessage o = new OutputObjectMessage
            {
                backColor = bc,
                foreColor = fc,
                newLine = NewLine,
                Message = forOutput.ToString()/* Message.Replace("\r\n", "\n").Replace("\n","<br/>").Replace(" ", "&nbsp;")*/
            };

            if( stopEvent.WaitOne(0) )
                return;
            outputQueue.Enqueue(o);
        }

        public BufferCell[,] GetBufferContents(System.Management.Automation.Host.Rectangle rectangle)
        {
            return outBuffer.GetBufferRect(rectangle);
        }

        public System.Management.Automation.Host.Size BufferSize
        {
            get
            {
                return outBuffer.Size;
            }
            set
            {
                Trace("Set buffer size: height={0}, width={1}", value.Height, value.Width);
                outBuffer.Size = value;
            }
        }

        internal void ClearBuffer()
        {
            outBuffer = new Buffer();
        }

        public Coordinates CursorPosition
        {
            get
            {
                return cursorPos;
            }
            set
            {
                Trace("Set cursor position: X={0}, Y={1}", value.X, value.Y);
                cursorPos = value;
            }
        }


        void IDisposable.Dispose()
        {
            stopEvent.Set();

            while( workerThread.ThreadState != ThreadState.Aborted && workerThread.ThreadState != ThreadState.Stopped )
                Thread.Sleep(1000);

            PS = null;
        }

        bool WebPSConsoleApp.ShouldExit
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        int WebPSConsoleApp.ExitCode
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public bool IsPowershellRunning
        {
            get
            {
                return powerShellIsRunning;
            }
        }

        internal void AddOutputObject(OutputObject o)
        {
            if( o is OutputObjectUserRequest )
                lastUserRequest = (OutputObjectUserRequest)o;
            outputQueue.Enqueue(o);
        }

        internal string BufferToHtml()
        {
            Trace("Send buffer as HTML");
            return outBuffer.ToHtml();
        }

        internal ConsoleColor DefaultForegroundColor
        {
            get;
            set;
        }

        internal ConsoleColor DefaultBackgroundColor
        {
            get;
            set;
        }

        internal void EndUserResponseMode()
        {
            lastUserRequest = null;
        }

        internal void ResendUserRequest()
        {
            if( lastUserRequest != null )
            {
                outputQueue.Enqueue(lastUserRequest);
            }
        }

        internal bool IsInUserRequestMode
        {
            get
            {
                return lastUserRequest != null;
            }
        }

        internal TimeSpan LastInput
        {
            get
            {
                return DateTime.Now.Subtract(lastInput);
            }
        }
    }
}